"use client"; /** * /w/:workspace/settings/automations — the AUTOMATION LIST. * * A table of the workspace's automations (name, status, trigger types, latest * run status) with a "New automation" button and per-row controls: an * enable/pause switch (ACTIVE ↔ PAUSED, via POST /automations/:id/status) and * a delete (DELETE /automations/:id → archives). Each row can expand its run * history (GET /automations/:id/runs). */ import { useState } from "react"; import { useParams, useRouter } from "next/navigation"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { History, Plus, Trash2 } from "lucide-react"; import { toast } from "@/stores/ui-store"; import { apiFetch } from "@/lib/api"; import { useWorkspace } from "@/hooks/use-workspace"; import { SettingsShell, Section } from "@/components/settings/settings-shell"; import { statusCls, runStatusLabel, type AutomationSummary } from "@/components/automations/lib"; import { RunHistory } from "@/components/automations/run-history"; export default function AutomationsListPage() { const params = useParams<{ workspace: string }>(); const slugOrId = params?.workspace ?? ""; const router = useRouter(); const { data: ws, isLoading } = useWorkspace(slugOrId); const wid = ws?.id ?? ""; return ( {isLoading || !ws ? (

Loading…

) : ( router.push(`/w/${slugOrId}/settings/automations/new`)} /> )}
); } function AutomationsTable({ workspaceSlug, workspaceId, onNew }: { workspaceSlug: string; workspaceId: string; onNew: () => void }) { const qc = useQueryClient(); const [cursor, setCursor] = useState(null); const [expanded, setExpanded] = useState(null); const list = useQuery({ queryKey: ["workspaces", workspaceId, "automations", cursor ?? "first"], queryFn: async (): Promise<{ data: AutomationSummary[]; nextCursor: string | null }> => { const res = await apiFetch<{ data: AutomationSummary[]; nextCursor: string | null }>( `/workspaces/${workspaceId}/automations?limit=50${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}` ); return res; }, enabled: !!workspaceId }); const rows = list.data?.data ?? []; const nextCursor = list.data?.nextCursor ?? null; const setStatus = useMutation({ mutationFn: (input: { id: string; status: string }) => apiFetch(`/automations/${input.id}/status`, { method: "POST", body: { status: input.status } }), onSuccess: () => qc.invalidateQueries({ queryKey: ["workspaces", workspaceId, "automations"] }), onError: (err) => toast("error", "Status change failed", err instanceof Error ? err.message : undefined) }); const remove = useMutation({ mutationFn: (id: string) => apiFetch(`/automations/${id}`, { method: "DELETE" }), onSuccess: () => { qc.invalidateQueries({ queryKey: ["workspaces", workspaceId, "automations"] }); }, onError: (err) => toast("error", "Delete failed", err instanceof Error ? err.message : undefined) }); return (
{rows.length} automation{rows.length === 1 ? "" : "s"}
{list.isLoading ? (

Loading…

) : (
{rows.length === 0 && ( )} {rows.map((a) => ( setExpanded(expanded === a.id ? null : a.id)} onStatus={(status) => setStatus.mutate({ id: a.id, status })} onRemove={() => { if (window.confirm(`Delete “${a.name}”? This archives it (runs are kept).`)) remove.mutate(a.id); }} busy={setStatus.isPending} /> ))}
Name Status Triggers Latest run
No automations yet. Create one to start automating.
)} {nextCursor && ( )}
); } function AutomationRow({ a, workspaceSlug, expanded, onToggleExpand, onStatus, onRemove, busy }: { a: AutomationSummary; workspaceSlug: string; expanded: boolean; onToggleExpand: () => void; onStatus: (status: string) => void; onRemove: () => void; busy: boolean; }) { const router = useRouter(); const active = a.status === "ACTIVE"; return ( <> {a.description ? {a.description} : null} {a.status} {a.triggers.length > 0 ? a.triggers.join(", ") : "—"} {a.latestRun ? ( {runStatusLabel(a.latestRun.status)} ) : ( never )}
{expanded && ( )} ); }